
#ifndef __view_table_view_h__
#define __view_table_view_h__

#pragma once

#include <windows.h>

#include <map>
#include <vector>

#include "SkColor.h"

#include "ui_base/keycodes/keyboard_codes_win.h"
#include "ui_base/models/table_model_observer.h"

#include "view/controls/native_control.h"

typedef struct tagNMLVCUSTOMDRAW NMLVCUSTOMDRAW;

class SkBitmap;

namespace gfx
{
    class Font;
}

namespace ui
{
    struct TableColumn;
    class TableModel;
}

// A TableView is a view that displays multiple rows with any number of columns.
// TableView is driven by a TableModel. The model returns the contents
// to display. TableModel also has an Observer which is used to notify
// TableView of changes to the model so that the display may be updated
// appropriately.
//
// TableView itself has an observer that is notified when the selection
// changes.
//
// Tables may be sorted either by directly invoking SetSortDescriptors or by
// marking the column as sortable and the user doing a gesture to sort the
// contents. TableView itself maintains the sort so that the underlying model
// isn't effected.
//
// When a table is sorted the model coordinates do not necessarily match the
// view coordinates. All table methods are in terms of the model. If you need to
// convert to view coordinates use model_to_view.
//
// Sorting is done by a locale sensitive string sort. You can customize the
// sort by way of overriding CompareValues.
//
// TableView is a wrapper around the window type ListView in report mode.
namespace view
{

    class ListView;
    class ListViewParent;
    class TableView;
    class TableViewObserver;

    // The cells in the first column of a table can contain:
    // - only text
    // - a small icon (16x16) and some text
    // - a check box and some text
    enum TableTypes
    {
        TEXT_ONLY = 0,
        ICON_AND_TEXT,
        CHECK_BOX_AND_TEXT
    };

    // Returned from SelectionBegin/SelectionEnd
    class TableSelectionIterator
    {
    public:
        TableSelectionIterator(TableView* view, int view_index);
        TableSelectionIterator& operator=(const TableSelectionIterator& other);
        bool operator==(const TableSelectionIterator& other);
        bool operator!=(const TableSelectionIterator& other);
        TableSelectionIterator& operator++();
        int operator*();

    private:
        void UpdateModelIndexFromViewIndex();

        TableView* table_view_;
        int view_index_;

        // The index in terms of the model. This is returned from the * operator. This
        // is cached to avoid dependencies on the view_to_model mapping.
        int model_index_;
    };

    // TODO(port): Port TableView.
    class TableView : public NativeControl, public ui::TableModelObserver
    {
    public:
        typedef TableSelectionIterator iterator;

        // A helper struct for GetCellColors. Set |color_is_set| to true if color is
        // set.  See OnCustomDraw for more details on why we need this.
        struct ItemColor
        {
            bool color_is_set;
            SkColor color;
        };

        // Describes a sorted column.
        struct SortDescriptor
        {
            SortDescriptor() : column_id(-1), ascending(true) {}
            SortDescriptor(int column_id, bool ascending)
                : column_id(column_id), ascending(ascending) {}

            // ID of the sorted column.
            int column_id;

            // Is the sort ascending?
            bool ascending;
        };

        typedef std::vector<SortDescriptor> SortDescriptors;

        // Creates a new table using the model and columns specified.
        // The table type applies to the content of the first column (text, icon and
        // text, checkbox and text).
        // When autosize_columns is true, columns always fill the available width. If
        // false, columns are not resized when the table is resized. An extra empty
        // column at the right fills the remaining space.
        // When resizable_columns is true, users can resize columns by dragging the
        // separator on the column header.  NOTE: Right now this is always true.  The
        // code to set it false is still in place to be a base for future, better
        // resizing behavior (see http://b/issue?id=874646 ), but no one uses or
        // tests the case where this flag is false.
        // Note that setting both resizable_columns and autosize_columns to false is
        // probably not a good idea, as there is no way for the user to increase a
        // column's size in that case.
        TableView(ui::TableModel* model, const std::vector<ui::TableColumn>& columns,
            TableTypes table_type, bool single_selection,
            bool resizable_columns, bool autosize_columns);
        virtual ~TableView();

        // Assigns a new model to the table view, detaching the old one if present.
        // If |model| is NULL, the table view cannot be used after this call. This
        // should be called in the containing view's destructor to avoid destruction
        // issues when the model needs to be deleted before the table.
        void SetModel(ui::TableModel* model);
        ui::TableModel* model() const { return model_; }

        // Resorts the contents.
        void SetSortDescriptors(const SortDescriptors& sort_descriptors);

        // Current sort.
        const SortDescriptors& sort_descriptors() const { return sort_descriptors_; }

        // Returns the number of rows in the TableView.
        int RowCount() const;

        // Returns the number of selected rows.
        int SelectedRowCount();

        // Selects the specified item, making sure it's visible.
        void Select(int model_row);

        // Sets the selected state of an item (without sending any selection
        // notifications). Note that this routine does NOT set the focus to the
        // item at the given index.
        void SetSelectedState(int model_row, bool state);

        // Sets the focus to the item at the given index.
        void SetFocusOnItem(int model_row);

        // Returns the first selected row in terms of the model.
        int FirstSelectedRow();

        // Returns true if the item at the specified index is selected.
        bool IsItemSelected(int model_row);

        // Returns true if the item at the specified index has the focus.
        bool ItemHasTheFocus(int model_row);

        // Returns an iterator over the selection. The iterator proceeds from the
        // last index to the first.
        //
        // NOTE: the iterator iterates over the visual order (but returns coordinates
        // in terms of the model).
        iterator SelectionBegin();
        iterator SelectionEnd();

        // TableModelObserver methods.
        virtual void OnModelChanged();
        virtual void OnItemsChanged(int start, int length);
        virtual void OnItemsAdded(int start, int length);
        virtual void OnItemsRemoved(int start, int length);

        void SetObserver(TableViewObserver* observer)
        {
            table_view_observer_ = observer;
        }
        TableViewObserver* observer() const { return table_view_observer_; }

        // Replaces the set of known columns without changing the current visible
        // columns.
        void SetColumns(const std::vector<ui::TableColumn>& columns);
        void AddColumn(const ui::TableColumn& col);
        bool HasColumn(int id);

        // Sets which columns (by id) are displayed.  All transient size and position
        // information is lost.
        void SetVisibleColumns(const std::vector<int>& columns);
        void SetColumnVisibility(int id, bool is_visible);
        bool IsColumnVisible(int id) const;

        // Resets the size of the columns based on the sizes passed to the
        // constructor. Your normally needn't invoked this, it's done for you the
        // first time the TableView is given a valid size.
        void ResetColumnSizes();

        // Sometimes we may want to size the TableView to a specific width and
        // height.
        virtual gfx::Size GetPreferredSize();
        void SetPreferredSize(const gfx::Size& size);

        // Is the table sorted?
        bool is_sorted() const { return !sort_descriptors_.empty(); }

        // Maps from the index in terms of the model to that of the view.
        int ModelToView(int model_index) const;

        // Maps from the index in terms of the view to that of the model.
        int ViewToModel(int view_index) const;

        // Sets the text to display on top of the table. This is useful if the table
        // is empty and you want to inform the user why.
        void SetAltText(const std::wstring& alt_text);

    protected:
        // Overriden to return the position of the first selected row.
        virtual gfx::Point GetKeyboardContextMenuLocation();

        // Subclasses that want to customize the colors of a particular row/column,
        // must invoke this passing in true. The default value is false, such that
        // GetCellColors is never invoked.
        void SetCustomColorsEnabled(bool custom_colors_enabled);

        // Notification from the ListView that the selected state of an item has
        // changed.
        virtual void OnSelectedStateChanged();

        // Notification from the ListView that the used double clicked the table.
        virtual void OnDoubleClick();

        // Notification from the ListView that the user middle clicked the table.
        virtual void OnMiddleClick();

        // Overridden from NativeControl. Notifies the observer.
        virtual bool OnKeyDown(ui::KeyboardCode virtual_keycode);

        // View override.
        virtual void OnBoundsChanged(const gfx::Rect& previous_bounds);

        // Invoked to customize the colors or font at a particular cell. If you
        // change the colors or font, return true. This is only invoked if
        // SetCustomColorsEnabled(true) has been invoked.
        virtual bool GetCellColors(int model_row,
            int column,
            ItemColor* foreground,
            ItemColor* background,
            LOGFONT* logfont);

        // Subclasses that want to perform some custom painting (on top of the regular
        // list view painting) should return true here and implement the PostPaint
        // method.
        virtual bool ImplementPostPaint() { return false; }
        // Subclasses can implement in this method extra-painting for cells.
        virtual void PostPaint(int model_row, int column, bool selected,
            const gfx::Rect& bounds, HDC device_context) {}
        virtual void PostPaint() {}

        virtual HWND CreateNativeControl(HWND parent_container);

        virtual LRESULT OnNotify(int w_param, LPNMHDR l_param);

        // Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating
        // whether the row2 comes before row1, row2 is the same as row1 or row1 comes
        // after row2. This invokes CompareValues on the model with the sorted column.
        virtual int CompareRows(int model_row1, int model_row2);

        // Called before sorting. This does nothing and is intended for subclasses
        // that need to cache state used during sorting.
        virtual void PrepareForSort() {}

        // Returns the width of the specified column by id, or -1 if the column isn't
        // visible.
        int GetColumnWidth(int column_id);

        // Returns the offset from the top of the client area to the start of the
        // content.
        int content_offset() const { return content_offset_; }

        // Draws the alt_text_. Does nothing if there is no alt_text_.
        void PaintAltText();

        // Size (width and height) of images.
        static const int kImageSize;

    private:
        // Direction of a sort.
        enum SortDirection
        {
            ASCENDING_SORT,
            DESCENDING_SORT,
            NO_SORT
        };

        // We need this wrapper to pass the table view to the windows proc handler
        // when subclassing the list view and list view header, as the reinterpret
        // cast from GetWindowLongPtr would break the pointer if it is pointing to a
        // subclass (in the OO sense of TableView).
        struct TableViewWrapper
        {
            explicit TableViewWrapper(TableView* view) : table_view(view) {}
            TableView* table_view;
        };

        friend class ListViewParent;
        friend class TableSelectionIterator;

        LRESULT OnCustomDraw(NMLVCUSTOMDRAW* draw_info);

        // Invoked when the user clicks on a column to toggle the sort order. If
        // column_id is the primary sorted column the direction of the sort is
        // toggled, otherwise column_id is made the primary sorted column.
        void ToggleSortOrder(int column_id);

        // Updates the lparam of each of the list view items to be the model index.
        // If length is > 0, all items with an index >= start get offset by length.
        // This is used during sorting to determine how the items were sorted.
        void UpdateItemsLParams(int start, int length);

        // Does the actual sort and updates the mappings (view_to_model and
        // model_to_view) appropriately.
        void SortItemsAndUpdateMapping();

        // Selects multiple items from the current view row to the marked view row
        // (implements shift-click behavior). |view_index| is the most recent row
        // that the user clicked on, and so there is no guarantee that
        // |view_index| > |mark_view_index| or vice-versa. Returns false if the
        // selection attempt was rejected because it crossed group boundaries.
        bool SelectMultiple(int view_index, int mark_view_index);

        // Method invoked by ListView to compare the two values. Invokes CompareRows.
        static int CALLBACK SortFunc(LPARAM model_index_1_p,
            LPARAM model_index_2_p, LPARAM table_view_param);

        // Method invoked by ListView when sorting back to natural state. Returns
        // model_index_1_p - model_index_2_p.
        static int CALLBACK NaturalSortFunc(LPARAM model_index_1_p,
            LPARAM model_index_2_p, LPARAM table_view_param);

        // Resets the sort image displayed for the specified column.
        void ResetColumnSortImage(int column_id, SortDirection direction);

        // Adds a new column.
        void InsertColumn(const ui::TableColumn& tc, int index);

        // Update headers and internal state after columns have changed
        void OnColumnsChanged();

        // Updates the ListView with values from the model. See UpdateListViewCache0
        // for a complete description.
        // This turns off redrawing, and invokes UpdateListViewCache0 to do the
        // actual updating.
        void UpdateListViewCache(int start, int length, bool add);

        // Updates ListView with values from the model.
        // If add is true, this adds length items starting at index start.
        // If add is not true, the items are not added, the but the values in the
        // range start - [start + length] are updated from the model.
        void UpdateListViewCache0(int start, int length, bool add);

        // Returns the index of the selected item before |view_index|, or -1 if
        // |view_index| is the first selected item.
        //
        // WARNING: this returns coordinates in terms of the view, NOT the model.
        int PreviousSelectedViewIndex(int view_index);

        // Returns the last selected view index in the table view, or -1 if the table
        // is empty, or nothing is selected.
        //
        // WARNING: this returns coordinates in terms of the view, NOT the model.
        int LastSelectedViewIndex();

        // The TableColumn visible at position pos.
        const ui::TableColumn& GetColumnAtPosition(int pos);

        // Window procedure of the list view class. We subclass the list view to
        // ignore WM_ERASEBKGND, which gives smoother painting during resizing.
        static LRESULT CALLBACK TableWndProc(HWND window, UINT message,
            WPARAM w_param, LPARAM l_param);

        // Window procedure of the header class. We subclass the header of the table
        // to disable resizing of columns.
        static LRESULT CALLBACK TableHeaderWndProc(HWND window, UINT message,
            WPARAM w_param, LPARAM l_param);

        // Updates content_offset_ from the position of the header.
        void UpdateContentOffset();

        // Reloads the groups from the model if there is one and it has groups.
        void UpdateGroups();

        // Returns the bounds of the alt text.
        gfx::Rect GetAltTextBounds();

        // Returns the font used for alt text.
        gfx::Font GetAltTextFont();

        // Overriden in order to update the column sizes, which can only be sized
        // accurately when the native control is available.
        virtual void VisibilityChanged(View* starting_from, bool is_visible);

        ui::TableModel* model_;
        TableTypes table_type_;
        TableViewObserver* table_view_observer_;

        // An ordered list of id's into all_columns_ representing current visible
        // columns.
        std::vector<int> visible_columns_;

        // Mapping of an int id to a TableColumn representing all possible columns.
        std::map<int, ui::TableColumn> all_columns_;

        // Cached value of columns_.size()
        int column_count_;

        // Selection mode.
        bool single_selection_;

        // If true, any events that would normally be propagated to the observer
        // are ignored. For example, if this is true and the selection changes in
        // the listview, the observer is not notified.
        bool ignore_listview_change_;

        // Reflects the value passed to SetCustomColorsEnabled.
        bool custom_colors_enabled_;

        // Whether or not columns should automatically be resized to fill the
        // the available width when the list view is resized.
        bool autosize_columns_;

        // Whether or not the user can resize columns.
        bool resizable_columns_;

        // Whether the column sizes have been determined.
        bool column_sizes_valid_;

        // NOTE: While this has the name View in it, it's not a view. Rather it's
        // a wrapper around the List-View window.
        HWND list_view_;

        // The list view's header original proc handler. It is required when
        // subclassing.
        WNDPROC header_original_handler_;

        // Window procedure of the listview before we subclassed it.
        WNDPROC original_handler_;

        // A wrapper around 'this' used when "subclassing" the list view and header.
        TableViewWrapper table_view_wrapper_;

        // A custom font we use when overriding the font type for a specific cell.
        HFONT custom_cell_font_;

        // The preferred size of the table view.
        gfx::Size preferred_size_;

        int content_offset_;

        // Current sort.
        SortDescriptors sort_descriptors_;

        // Mappings used when sorted.
        scoped_array<int> view_to_model_;
        scoped_array<int> model_to_view_;

        string16 alt_text_;

        DISALLOW_COPY_AND_ASSIGN(TableView);
    };

} //namespace view

#endif //__view_table_view_h__